{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Семинар 4. Классы. Продолжение."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Виртуальные функции"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Какая функция будет вызвана. Нарисовать что будет происходить в памяти"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class A\n",
    "{\n",
    "public:\n",
    "\t~A() {}\n",
    "\n",
    "\tvoid f() {}\n",
    "\tvirtual void g() {}\n",
    "\tvoid h() {}\n",
    "};\n",
    "\n",
    "class B : public A\n",
    "{\n",
    "public:\n",
    "\tvirtual ~B() {}\n",
    "\n",
    "\tvirtual void f() {}\n",
    "\tvoid g() {}\n",
    "\tvoid h() {}\n",
    "};\n",
    "\n",
    "class C : public B\n",
    "{\n",
    "public:\n",
    "\t~C() {}\n",
    "\n",
    "\tvoid f() {}\n",
    "\tvoid g() {}\n",
    "\tvirtual void h() {}\n",
    "};\n",
    "\n",
    "int main()\n",
    "{\n",
    "\t{\n",
    "\t\tC c;\n",
    "\t\tc.f(); c.g(); C.h();\n",
    "\n",
    "\t\tB b = c;\n",
    "\t\tb.f(); b.g(); b.h();\n",
    "\n",
    "\t\tA& ar = c;\n",
    "\t\tar.f(); ar.g(); ar.h();\n",
    "\t}\n",
    "\n",
    "\t{\n",
    "\t\tC* cp = new C;\n",
    "\t\tA* ap = cp;\n",
    "\t\tap->f(); ap->g(); ap->h();\n",
    "\t\tdelete ap;\n",
    "\t}\n",
    "\n",
    "\t{\n",
    "\t\tC* cp = new C;\n",
    "\t\tB* bp = cp;\n",
    "\t\tdelete bp;\n",
    "\t}\n",
    "\n",
    "\treturn 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Какая функция будет вызвана?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class A {\n",
    "public:\n",
    "    A() {\n",
    "        init();\n",
    "    }\n",
    "\n",
    "    ~A() {\n",
    "        deinit();\n",
    "    }\n",
    "\n",
    "    virtual void init();\n",
    "    virtual void deinit();\n",
    "};\n",
    "\n",
    "class B : public A {\n",
    "public:\n",
    "    B() {}\n",
    "    ~B() {}\n",
    "\n",
    "    void init() override { ... }\n",
    "    void deinit() override { ... }\n",
    "};\n",
    "\n",
    "int main() {\n",
    "    B b;\n",
    "    return 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Виртуальные деструкторы: зачем и как"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы уже познакомились с механизмом виртуальных функций и поняли зачем и как они вызываются. Насчёт деструкторов рассмотрим такой пример:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    int age;\n",
    "    \n",
    "public:\n",
    "    virtual void cry() = 0;\n",
    "};\n",
    "\n",
    "class Dog : public Animal\n",
    "{\n",
    "    std::string name;\n",
    "    \n",
    "public:\n",
    "    void cry() override;\n",
    "};\n",
    "\n",
    "class Human : public Animal\n",
    "{\n",
    "    std::string name;\n",
    "    std::string surname;\n",
    "    \n",
    "public:\n",
    "    void cry() override;\n",
    "};\n",
    "\n",
    "\n",
    "void func()\n",
    "{\n",
    "    Animal* animal = nullptr;\n",
    "    if (std::rand() % 2)\n",
    "        animal = new Dog;\n",
    "    else\n",
    "        animal = new Human;    \n",
    "    ...;    \n",
    "    delete animal;  // что здесь делать компилятору?\n",
    "}\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Поэтому реализуем иерархии так:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class Animal\n",
    "{\n",
    "    int age;\n",
    "\n",
    "public:\n",
    "    virtual ~Animal() = default;  // такой деструктор - ВИРТУАЛЬНЫЙ метод\n",
    "    virtual void cry() = 0;\n",
    "};\n",
    "\n",
    "// далее как обычно\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### const-методы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Упражнение: какая функция будет вызвана"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class A\n",
    "{\n",
    "public:\n",
    "\tvoid f() { std::cout << \"f();\"; }\n",
    "\tvoid f() const { std::cout << \"f() const;\" }\n",
    "};\n",
    "\n",
    "\n",
    "int main()\n",
    "{\n",
    "\tA a;\n",
    "\ta.f();\n",
    "\n",
    "\tconst A ca;\n",
    "\tca.f();\n",
    "\n",
    "\tconst A& car = a;\n",
    "\tcar.f();\n",
    "\n",
    "\tA& ar = a;\n",
    "\tar.f();\n",
    "\n",
    "\tA* const acp = &a;\n",
    "\tacp->f();\n",
    "\n",
    "\tconst A* cap = &a;\n",
    "\tcap->f();\n",
    "\n",
    "\tconst A a2 = a;\n",
    "\ta2.f();\n",
    "\n",
    "\treturn 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### class vs struct"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* `class` - если есть инвариант\n",
    "* `struct` - если поля независимы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "struct Point\n",
    "{\n",
    "\tdouble x;\n",
    "\tdouble y;\n",
    "};\n",
    "\n",
    "struct Color\n",
    "{\n",
    "\tchar r;\n",
    "\tchar g;\n",
    "\tchar b;\n",
    "};\n",
    "\n",
    "// ???\n",
    "struct Size\n",
    "{\n",
    "\tdouble w;\n",
    "\tdouble h;\n",
    "};\n",
    "\n",
    "struct Rectangle\n",
    "{\n",
    "\tPoint origin;\n",
    "\tSize size;\n",
    "};\n",
    "\n",
    "class JuiceBottle\n",
    "{\n",
    "private:\n",
    "\tdouble max_volume_;\n",
    "\tdouble cur_volume_;\n",
    "};\n",
    "\n",
    "class String\n",
    "{\n",
    "private:\n",
    "\tchar *s_;\n",
    "\tsize_t len_;\n",
    "\tsize_t capacity_;\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Принципы дизайна класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* RAII\n",
    "* Есть инвариант - class, поля независимы - struct\n",
    "* Если есть подклассы - делаем `virtual` деструктор\n",
    "* Правило 6\n",
    "    * `=default` где достаточно автогенерённого\n",
    "    * `=delete` где нужно запретить\n",
    "    \n",
    "    ```c++\n",
    "    class Animal\n",
    "    {\n",
    "    private:\n",
    "        std::string name;\n",
    "\n",
    "    public:\n",
    "        Animal() : name(\"unknown\") {}\n",
    "        Animal(const Animal&) = default;\n",
    "        Animal(Animal&&) noexcept = delete;\n",
    "        Animal& operator= (const Animal&) = default;\n",
    "        Animal& operator= (Animal&&) = delete;\n",
    "        ~Animal() noexcept = default;\n",
    "    };\n",
    "    ```\n",
    "\n",
    "* `noexcept` d-tor, move operators (подробнее на лекции про исключения)\n",
    "* `const` на все методы, которые не меняют состояния класса (значения полей)\n",
    "\n",
    "    ```c++\n",
    "    class Animal\n",
    "    {\n",
    "    private:\n",
    "        std::string name;\n",
    "\n",
    "    public:\n",
    "        const std::string& get_name() const noexcept { return name; }\n",
    "    };\n",
    "    ```\n",
    "\n",
    "* `override` на переопределённые `virtual` методы\n",
    "\n",
    "    ```c++\n",
    "    class Animal\n",
    "    {\n",
    "    public:\n",
    "        virtual void say() = 0;\n",
    "    };\n",
    "\n",
    "    class Cat : public Animal\n",
    "    {\n",
    "    public:\n",
    "        void say() override;\n",
    "    };\n",
    "    ```\n",
    "\n",
    "* Какой функционал делать методом, а какой - свободной фунцкией? Всопним ADL. Благодаря ему есть философский принцип, что функции, принимающие первым аргументом объект класса (или ссылку), являются частью интерфейса класса:\n",
    "\n",
    "    ```c++\n",
    "    struct Vector3D\n",
    "    {\n",
    "        double x, y, z;\n",
    "    };\n",
    "\n",
    "    // философски часть интерфейса класса Vector3D\n",
    "    double length(const Vector3D v);\n",
    "    ```\n",
    "\n",
    "    Поэтому принцип рационального минмимума в методах:\n",
    "      * struct Vector3D - всё в свободные функции (normalize, length, rotate ...))\n",
    "      * vector<int> (pointer, size, capacity: size() - member; at() - member; sort() - function; empty() - ???; middle element - function; max() - function, mean() - function)\n",
    "      * cache (add, get, del; clear? - в метод, т.к. эффективнее реализовать, обладая доступом до полей)\n",
    "\n",
    "    Нужна __причина__, чтобы что-нибудь сделать методом:\n",
    "        * требуется доступ до приватных полей и нельзя реализовать иначе (vector.size(), vector.at(), vector.push_back())\n",
    "        * реализация через приватные поля проще и быстрее (cache.clear())\n",
    "        * лучше вписывается в сщуествующие методы класса (vector.empty())\n",
    "        * что-нибудь ещё?\n",
    "\n",
    "* Никогда не используйте `protected`-поля\n",
    "\n",
    "* Иногда нужно менять поля в `const`-методах, и это не влияет на состояние класса в умозрительном смысле. Используйте `mutable`:\n",
    "    * Кеширование: бинарное дерево, которое запоминает последний запрос\n",
    "    * `mutex`\n",
    "    * отложенная инициализация\n",
    "\n",
    "* SRP - Single Responsibility Principle - Принцип единственной ответственности - класс должен быть экспертом в одной и только одной области. Такие классы проще отлаживать и тестировать. Всегда понятно, ошибка в рамках его области ответственности или нет.\n",
    "* Liskov Substitution Principle - Принцип подстановки Барбары Лисков - наследник класса всегда можно передать в алгоритм вместо базового, при этом поведение алгоритма не изменится. Другими словами: наследники расширяют поведение базовых классов, но не изменяют его."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
